#pragma once

#include <iostream>
#include <string>
#include <strings.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>

#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"

using namespace std;

namespace Server
{
    enum
    {
        USAGE_ERROR = 1,
        SOCKET_ERROR,
        BIND_ERROR,
        LISTEN_ERROR
    };

    class tcpServer; // 声明
    class ThreadData
    {
    public:
        ThreadData(tcpServer *self, int &sockfd) : _self(self), _sockfd(sockfd)
        {
        }

    public:
        tcpServer *_self;
        int _sockfd;
    };

    static const uint16_t g_port = 3000; // 服务器要开放的端口
    static const int g_backlog = 5;
    // static const int g_num = 1024; // 已在Task.hpp中定义

    class tcpServer
    {
    public:
        tcpServer(const uint16_t port = g_port) : _port(port), _listen_sockfd(-1)
        {
        }
        ~tcpServer() {}

        void init()
        {
            // 1. 创建套接字
            _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0); // TCP通信
            if (_listen_sockfd == -1)
            {
                logMessage(FATAL, "create socket error");
                exit(SOCKET_ERROR);
            }
            logMessage(NORMAL, "create socket success: %d", _listen_sockfd);

            // 2. bind网络信息
            struct sockaddr_in local;
            bzero(&local, 0);

            local.sin_family = AF_INET;
            local.sin_addr.s_addr = INADDR_ANY;
            local.sin_port = htons(_port);
            /*
            只有创建套接字，绑定这里才需要做主机转网络操作，其他例如recvfrom、sendto时不需要，因为：
            recvfrom和sendto都是系统调用，对于系统调用，用户不必知道是怎样实现的，系统调用自己在内部就会完成主机序列和网络序列的转换。
            而bind的参数是结构体， 绑定的IP,PORT等信息是用户自己手动设置的数据，所有在进行网络通信前，要进行主机序列转网络序列
            */

            int n = bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local));
            if (n == -1)
            {
                logMessage(FATAL, "bind error");
                exit(BIND_ERROR);
            }
            logMessage(NORMAL, "bind success");

            // 3. TCP面向连接——通信前先建立连接——设置socket为监听状态
            n = listen(_listen_sockfd, g_backlog); // TODO
            if (n == -1)
            {
                logMessage(FATAL, "listen socket error");
                exit(LISTEN_ERROR);
            }
            logMessage(NORMAL, "listen socket success");
        }
        void start()
        {
            /* 服务器一经启动，应该是不受用户登录退出的影响，这样的进程就是守护进程 */

            // 4.0 初始化线程池
            ThreadPool<Task>::getSingleInstance()->run();
            logMessage(NORMAL, "thread pool init success");

            // 测试日志
            // logMessage(DEBUG, "DEBUG LOG TEST");
            // logMessage(DEBUG, "DEBUG LOG TEST");
            // logMessage(DEBUG, "DEBUG LOG TEST");
            // logMessage(WARNING, "WARNING LOG TEST");
            // logMessage(ERROR, "ERROR LOG TEST");
            // logMessage(ERROR, "ERROR LOG TEST");

            // signal(SIGCHLD, SIG_IGN); // b. 2
            while (true)
            {
                // 4. server端获取新连接
                struct sockaddr_in from;
                bzero(&from, 0);
                socklen_t fromlen = sizeof(from);

                // sockfd，未来和客户端进行通信的套接字fd
                int sockfd = accept(_listen_sockfd, (struct sockaddr *)&from, &fromlen);
                if (sockfd == -1)
                {
                    logMessage(ERROR, "accept error, to do next accept");
                    continue; // 当前服务错误，不影响运行
                }
                logMessage(NORMAL, "accept a new link success, get new socket: %d", sockfd); // TODO
                cout << "sockfd: " << sockfd << endl;

                // 5. 通信
                // 使用获取到的新连接对应的套接字通信
                // TCP是面向字节流的，所以，从这里开始，之后的操作都是文件操作

                // a. version 1
                // serviceIO(sockfd);
                // close(sockfd);// 对于一个使用完的套接字，要关闭它对应的文件。
                /* 一个套接字相当于一个文件，文件描述符本质就是数组下标，数组的长度是有上限的，所以文件描述符的数量有上限，
                所以，当创建太多套接字而不关闭它们，就可能造成栈溢出，服务器就会崩溃 ，这叫做文件描述符泄露*/
                // ulimit -a命名查看linux下的打开的文件个数上限。我这台机器是65535，代表一个服务器（进程）最多能打开65535个文件

                // b. version 2 多进程
                //
                // //      1. 不使用signal(SIGCHLD, SIG_IGN);
                // // pid_t id = fork();
                // // if(id == 0)
                // // {
                // //     // child process
                // //
                // //     // 因为子进程会继承父进程的文件描述符，但这些文件描述符可能并不是被子进程都需要，所以最好关闭不需要的文件描述符
                // //     close(_listen_sockfd);// 提供服务，不需要监听
                // //
                // //     // 处理阻塞式等待
                // //     if(fork() > 0) exit(0);// child process  子进程退出后，父进程就等待成功，解除阻塞等待间接实现并发
                // //     // grandchild process
                // //
                // //     // 由孙子进程为客户端提供相应的服务
                // //     serviceIO(sockfd);
                // //     close(sockfd);
                // //
                // //     exit(0);// 孙子进程提供完服务就结束，因为子进程退出，所以孙子进程会成为孤儿进程，被OS“领养”
                // // }
                // // // father process
                // // // 我们的意愿是让[提供服务]这部分代码并行执行，所以，不能设置阻塞式待子进程
                // // // 这里也不能设置非阻塞等待，如果accept失败，就会产生僵尸进程
                // // pid_t ret = waitpid(id, nullptr, 0);
                // // if(ret > 0)
                // // {
                // //     cout << "wait process seccess : " << ret << endl;
                // // }
                //
                // //      2. 使用signal(SIGCHLD, SIG_IGN)
                // pid_t id = fork();
                // if(id == 0)
                // {
                //     // child process
                //
                //     // 因为子进程会继承父进程的文件描述符，但这些文件描述符可能并不是被子进程都需要，所以最好关闭不需要的文件描述符
                //     close(_listen_sockfd);// 提供服务，不需要监听
                //
                //     // 处理阻塞式等待
                //
                //     // 由子进程为客户端提供相应的服务
                //     serviceIO(sockfd);
                //     close(sockfd);
                //
                //     exit(0);// 子进程提供完服务就结束，因为子进程退出，所以孙子进程会成为孤儿进程，被OS“领养”
                // }
                // // father process
                // close(sockfd);
                // //  子进程和父进程共同占有sockfd，close的操作是让引用计数减一，
                // //  必须子进程和父进程都close(sockfd),sockfd才会被真正关闭/释放
                // // version 2 end

                // c. version 3 多线程
                // pthread_t tid;
                // ThreadData* td = new ThreadData(this, sockfd);
                // pthread_create(&tid, nullptr, threadRoutine, td);
                // // pthread_join(tid, nullptr);// 不能join阻塞等待，不然还是串行。解决方法：线程分离即可

                // d. version 4 线程池
                ThreadPool<Task>::getSingleInstance()->push(Task(sockfd, serviceIO));

                // natstat -alntp：查看tcp相关 all-listen-number-tcp-process
            }
        }

    private:
        // version 1 & 2 & 3
        // void serviceIO(int sockfd)
        // {
        //     char buffer[g_num];
        //     while(true)
        //     {
        //         // a. 接收
        //         // ssize_t read(int fd, void *buf, size_t count);
        //         ssize_t n = read(sockfd, buffer, sizeof(buffer) - 1);
        //         if(n > 0)
        //         {
        //             buffer[n] = 0;// 字符串
        //             cout << "receive message: " << buffer << endl;
        //
        //             string outBuffer = buffer;
        //             outBuffer += " [server echo]";
        //
        //             // b. 回送
        //             // ssize_t write(int fd, const void *buf, size_t count);
        //             write(sockfd, outBuffer.c_str(), outBuffer.size());// 多路转接再考虑write返回值问题
        //         }
        //         else if(n == 0)
        //         {
        //             // 走到这里，代表Client exit
        //             logMessage(NORMAL, "client exit, me too");
        //             break;
        //         }
        //     }
        // }

        // version 3 多线程
        // static void *threadRoutine(void *args)
        // {
        //     // 副线程执行
        //
        //     // 分离线程
        //     pthread_detach(pthread_self());
        //     //
        //     ThreadData *td = static_cast<ThreadData *>(args);
        //     td->_self->serviceIO(td->_sockfd);
        //     // 完成服务
        //     close(td->_sockfd);
        //     delete td;
        //
        //     return nullptr;
        // }

    private:
        int _listen_sockfd;
        uint16_t _port;
    };
}